/* 
自定义Promise
*/
((window) => {
  // 定义状态值常量
  const PENDING = 'pending';
  const FULFILLED = 'fulfilled';
  const REJECTED = 'rejected';

  /**
   * 构造函数
   * @param  excutor 执行器函数
   */

  /* 
    promise对象内部要存储哪些信息?
      1. 状态: pending/fulfilled/rejected  ==> state
      2. 结果数据: undefined/value/reason  ==> data
      3. 保存待处理回调的数组: [{resolved () {}, rejected () {}}, {resolved () {}, rejected () {}}] => callbacks
   
      在构造函数中, 会立即同步调用执行器函数, 并传入resolve和reject 两个函数

      resolve函数
        参数: value
        作用: 将promise的状态改为resolved, 将value保存到data
      reject函数
        参数: reason
        作用: 将promise的状态改为rejected, 将reason保存到data
    */
  function Promise(excutor) {
    /* 初始化3个属性 */
    // 1. 状态: pending/fulfilled/rejected  ==> state
    this.state = PENDING;
    // 2. 结果数据: undefined/value/reason  ==> data
    this.data = undefined;
    // 3. 保存待处理回调的数组: [{resolved () {}, rejected () {}}, {resolved () {}, rejected () {}}] => callbacks
    this.callbacks = [];

    const self = this;

    /* 
    resolve函数
        参数: value
        作用: 将promise的状态改为resolved, 将value保存到data
    */
    function resolve(value) {
      // 只有当前状态为pendding, 才向下处理
      if (self.state!==PENDING) return

      // 改状态为成功的
      self.state = FULFILLED
      // 保存数据
      self.data = value
      // 异步执行callbacks中所有成功的回调
      // 我们没办法直接向微队列中添加回调 => 只能用宏队列了 setTimeout
      if (self.callbacks.length>0) {
        setTimeout(() => {
          self.callbacks.forEach(callbackObj => callbackObj.resolved(value))
        }, 0)
      }
    }

    /* 
  reject函数
      参数: reason
      作用: 将promise的状态改为rejected, 将reason保存到data
  */
    function reject(reason) {
      // 只有当前状态为pendding, 才向下处理
      if (self.state!==PENDING) return

      // 改状态为失败的
      self.state = REJECTED
      // 保存数据
      self.data = reason
      // 异步执行callbacks中所有成功的回调
      // 我们没办法直接向微队列中添加回调 => 只能用宏队列了 setTimeout
      if (self.callbacks.length>0) {
        setTimeout(() => {
          self.callbacks.forEach(callbackObj => callbackObj.rejected(reason))
        }, 0)
      }
    }

    // 捕获执行器抛出的错误
    try {
      /*  在构造函数中, 会立即同步调用执行器函数, 并传入resolve和reject 两个函数 */
      excutor(resolve, reject);
    } catch (error) {
      // 将当前promise变为rejected, reason为error
      reject(error)
    }
    
  }

  /**
   * 指定成功和失败的回调, 并返回一个新的promise
   * @param {*} onResolved 成功的回调 => 用于得到成功的value
   * @param {*} onRejected 失败的回调 => 用于得到失败的reason
   */
  /* 
   then返回的promise的结果由什么决定
		简单表达: 由then指定的回调执行的结果决定
      返回promise  ==> 得到并改为它的状态
      返回非promise  ==> 变为成功状态
      抛出错误   ==> try---catch  => 变为失败状态
  */
  Promise.prototype.then = function (onResolved, onRejected) {
    const self = this

    // 如果onResolved不是函数, 指定一个默认函数, 向下传递value
    onResolved = typeof onResolved==='function' ? onResolved : value => value
    // 如果onRejected不是函数, 指定一个默认函数, 向下传递reason
    onRejected = typeof onRejected==='function' ? onRejected : reason => {throw reason}

    return new Promise((resolve, reject) => {

      /**
       * 调用指定成功/失败的回调函数, 根据执行结果更新返回的promies的状态
       * @param {*} callback 
       */
      function handle(callback) {
        try {
          const result = callback(self.data)
          if (result instanceof Promise) { // 情况1: 返回promise  ==> 得到并改为它的状态
            result.then(
              value => resolve(value),
              reason => reject(reason)
            )
          } else { // 情况2: 返回非promise  ==> 变为成功状态
            resolve(result)
          }
        } catch (error) { // 情况3: 抛出错误   ==> try---catch  => 变为失败状态
          reject(error)
        }
      }

      // 如果当前是成功的, 异步执行onResolved
      if (self.state===FULFILLED) {
        setTimeout(() => {
          handle(onResolved)
        }, 0);
      } else if (self.state===REJECTED) {
        // 如果当前是失败的, 异步执行onRejected
        setTimeout(() => {
          handle(onRejected)
        }, 0);
      } else {
        // 如果当前是pending的, 保存onResolved和onRejected, 等到状态变为成功或失败时, 才执行
        self.callbacks.push({
          resolved: () => handle(onResolved),
          rejected: () => handle(onRejected)
        })
      }
    })
  };

  /**
   * 指定失败的回调, 并返回一个新的promise
   * 本质是then方法的语法糖(简洁语法)
   * then(value => value, onRejected)
   * @param {*} onRejected 失败的回调 => 用于得到失败的reason
   */
  Promise.prototype.catch = function (onRejected) {
    return this.then(value => value, onRejected)
  };

  /**
   *  返回一个成功的promise, value为指定的value
   * @param {*} value  可能是非promise, 也可能是promise
   */
  Promise.resolve = function (value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(
          value => resolve(value),
          reason => reject(reason)
        )
      } else {
        resolve(value)
      }
      
    })
  };

  /**
   *  返回一个失败的promise, reason为指定的reason
   * @param {*} reason
   */
  Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  };

  /**
   * 返回一个promise, 如果promises都成功, 它才成功, 成功的value为所有value组成的数组, 只要有一个失败, 那它就失败了
   * @param {*} promises
   */
  Promise.all = function (promises) {

    // 保存所有成功Promise的value的数组
    const values = []
    // 成功的数量
    let resolvedCount = 0 
    return new Promise((resolve, reject) => {
      // 遍历promises, 读取每个的结果值
      promises.forEach((p, index)=> {
        p.then(
          value => {
            // 保存当前成功的value
            values[index] = value
            // 成功的数量加1
            resolvedCount += 1
            // 如果当前是最后一个成功的, 将返回promise的状态改变为成功
            if (resolvedCount===promises.length) {
              resolve(values)
            }
          },
          reason => { // 一旦有一个promise失败了, 当前返回的promise就直接失败
            reject(reason)
          }
        )
      })
    })
  };

  /**
   * 返回一个promise, 由第一个完成的promie来决定它的结果
   * @param {*} promises
   */
  Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
      promises.forEach(p => {
        p.then(
          value => resolve(value),
          reason => reject(reason),
        )
      })
    })
  };

  /**
   * 返回一个promise, 当所有promise都完成时它就成功, value值为所有promise的value或reason组成的数组
   * @param {*} promises
   */
  Promise.allSettled = function (promises) {
    // 保存所有完成Promise的value/reason的数组
    const results = []
    // 完成的数量
    let settledCount = 0 

    // 返回的promise不会变为失败的, 只会变为成功的, 所有的都完成了, 就会成功, 成功value是所有promise的value或reason组成的数组
    return new Promise((resolve) => {
      promises.forEach((p, index) => {
        p.then(
          value => {
            // 保存当前成功value和成功状态的对象
            results[index] = {status: 'fulfilled', value}
            settledCount++
            if (settledCount===promises.length) {
              resolve(results)
            }
          },
          reason => {
            // 保存当前失败的reason和失败状态的对象
            results[index] = {status: 'rejected', reason}
            settledCount++
            if (settledCount===promises.length) {
              resolve(results)
            }
          }
        )
      })
    })
  };

  /* 
  返回一个迟延指定时间才成功的promise
  */
  Promise.resolveDelay = function (value, delay) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(value)
      }, delay);
    })
  }

  /* 
  返回一个迟延指定时间才失败的promise
  */
  Promise.rejectDelay = function (reason, delay) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(reason)
      }, delay);
    })
  }


  /* 
   finally(onFinal)
      无论当前promise是成功还是失败, onFinal都会执行, onFinal不接收参数
      finally也是返回一个新的promise, 结果状态由当前promise的结果状态决定
  */
  Promise.prototype.finally = function (onFinal) {
    const self = this
    return new Promise((resolve, reject) => {
      // 得到当前promise成功或失败的结果
      // 无论成功还是失败都调用onFinal
      // 如果成功, 将返回的promise变为成功
      // 如果失败, 将返回的promise变为失败
      self.then(
        value => {
          onFinal()
          resolve(value)
        },
        reason => {
          onFinal()
          reject(reason)
        }
      )
    })
  }

  window.Promise = Promise;
})(window);
